# -*- coding: utf-8 -*-
import copy
import random
from tools import nfp_utls
from settings import POPULATION_SIZE, MUTA_RATE
from tools.nfp_utls import almost_equal, rotate_polygon, get_polygon_bounds, polygon_area,nfp_rectangle

class genetic_algorithm():

    def __init__(self, segments_sorted_list, container):

        self.populationSize = POPULATION_SIZE  ## 种群大小
        self.mutationRate = MUTA_RATE ## 变异概率
        self.container = container

        self.population = [ {'solution': segments_sorted_list} ]
        for ind in range(self.populationSize):
            clone = [s for s in segments_sorted_list]
            for i in range(len(clone)):
                if random.random() < 0.2 and i > 0:
                    clone[i - 1], clone[i] = clone[i], clone[i - 1]
            clone_dict = {'solution': clone}
            self.population.append(clone_dict)


    ##################################################################################################################
    def mutate_individual(self, individual):
        clone = { 'solution': individual['solution'][:] }
        for i in range(len(clone['solution'])):
            if random.random() < self.mutationRate:
                if i + 1 < len(clone['solution']):
                    clone['solution'][i], clone['solution'][i + 1] = clone['solution'][i + 1],  clone['solution'][i]
        return clone

    ##################################################################################################################
    def generation(self):
        """
        :return:
        """
        # 适应度 从大到小排序
        self.select()
        self.mutate()
        #print("self.population = ", self.population)
        self.population = sorted(self.population, reverse=True, key=lambda a: a['fitness'])
        # for i in self.population:
        #     print("fitness= ", i['fitness'])
        new_population = self.population[:int(0.2 * len(self.population))]
        ###new_population = [self.population[0]]

        while len(new_population) < self.populationSize:
            ### 排在后面的population, 被选中为父母的概率越低
            male = self.random_weighted_individual()
            female = self.random_weighted_individual(male)

            ### 父亲随机选择一个cutpoint，剩下的从母亲那里取
            ### 母亲随机选择一个cutpoint，剩下的从父亲那里取
            children = self.mate(male, female)

            ### 孩子再经过变异
            new_population.append(self.mutate_individual(children[0]))
            if len(new_population) < self.populationSize:
                new_population.append(self.mutate_individual(children[1]))

        self.population = new_population


    def select(self):
        self.population = sorted(self.population, reverse=True, key=lambda a: a['fitness'])
        new_population = self.population[:int(0.25 * len(self.population))]

        for i in range(len(self.population)):
            if i < int(0.25 * len(self.population)):
                continue
            else:
                while i != len(self.population) - 1:
                    index = random.randint(int(0.5 * len(self.population)), len(self.population) - 1)
                    new_population.append(self.population[index])
                    break

        for i in range(len(new_population)):
            print("in select new_pop = ", new_population[i]['fitness'])

        self.population = new_population

    # def cross(self):
    #     rate = random.random()
    #     if rate > pcl and rate < pch:
    #
    #         while len(self.population) < self.populationSize:
    #
    #             male = self.random_weighted_individual()
    #             female = self.random_weighted_individual(male)
    #
    #             children = self.mate(male, female)
    #
    #             new_population.append(self.mutate(children[0]))
    #
    #             if len(new_population) < self.populationSize:
    #                 new_population.append(self.mutate(children[1]))

    def mutate(self):
        for individual in self.population:
            new_population = list()
            rate = random.random()
            if rate < MUTA_RATE:
                clone = { 'solution': individual['solution'][:] }
                for i in range(0, len(clone['solution'])):
                    if random.random() < self.mutationRate:
                        if i + 1 < len(clone['solution']):
                            clone['solution'][i], clone['solution'][i + 1] = clone['solution'][i + 1], clone['solution'][i]
                new_population.append(clone)
            else:
                new_population.append(individual)


    def random_weighted_individual(self, exclude=None):
        """
        :param exclude:
        :return:
        排在后面的pop 被选中的概率越小
        """
        pop = self.population
        if exclude and pop.index(exclude) >= 0:
            pop.remove(exclude)

        rand = random.random()
        lower = 0
        weight = 1.0 / len(pop)
        upper = weight
        pop_len = len(pop)
        for i in range(0, pop_len):
            if  lower < rand < upper:
                return pop[i]
            lower = upper
            upper +=  2 * weight * float(pop_len - i) / pop_len
        return pop[0]

    def mate(self, male, female):
        """

        :param male:
        :param female:
        :return:
        """
        cutpoint = random.randint(0, len(male['solution']) - 1)

        def contains(gene, shape_id):
            for i in range(0, len(gene)):
                if gene[i]['p_id'] == shape_id:
                    return True
            return False

        gene1 = male['solution'][:cutpoint]
        for i in range(len(female['solution']) - 1, -1, -1):
            if not contains(gene1, female['solution'][i]['p_id']):
                gene1.append(female['solution'][i])

        gene2 = female['solution'][:cutpoint]
        for i in range(len(male['solution']) - 1, -1, -1):
            if not contains(gene2, male['solution'][i]['p_id']):
                gene2.append(male['solution'][i])

        return [{'solution': gene1}, {'solution': gene2}]


